package com.badger.generator.pattern;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.badger.generator.entity.TableAttr;
import com.badger.generator.entity.TableField;
import com.badger.generator.enums.DataBaseFieldEnum;
import com.badger.generator.enums.DataBaseType;
import com.badger.generator.util.BuildTextUtils;

import lombok.Getter;
import lombok.Setter;

/**
 * 构建生成文件所需要的表的信息以及字段信息的数据
 * 策略模式
 * @author liqi
 *
 */
@Getter
@Setter
public abstract class AbstractDataBaseStrategy {
    /**主键标识*/
    public final static String PRI = "PRI";

    /**根据sql查询出的数据，不同的数据，对应的字段的key.*/
    private DataBaseFieldEnum fieldEnum;
    /**构建集合里的表的相关数据.*/
    private List<String> tables;
    /**根据表名，生成类名，去掉的表前缀的集合.*/
    private List<String> includePrefix;
    /**数据库连接.*/
    private Connection connection;

    /**
     * 构造
     * @param tables 生成的table的集合
     * @param includePrefix 根据表名，生成类名，去掉的表前缀的集合
     * @return
     */
    public AbstractDataBaseStrategy(final Connection connection, final DataBaseFieldEnum fieldEnum,
            final List<String> tables, final List<String> includePrefix) {
        this.fieldEnum = fieldEnum;
        this.tables = tables;
        this.includePrefix = includePrefix;
        this.connection = connection;
    }

    /**
     * 获取查询表结构的sql.
     * @return
     */
    abstract String getTableSql();

    /**
     * 根据表名，获取该表下，所有的字段信息的sql
     * @param tableName
     * @return
     */
    abstract String getFieldSql(String tableName);

    /**
     * 数据库类型转成java类型
     * @param fieldType
     * @return
     */
    abstract DataBaseType typeConvert(String fieldType);

    /**
     * 构建生成文件所需要的表的信息以及字段信息的数据
     * @return
     */
    public List<TableAttr> buildTableField() {
        List<TableAttr> listVo = new ArrayList<TableAttr>();
        try (Connection conn = this.connection) {
            Map<String, String> tableNameMap = new LinkedHashMap<>();
            PreparedStatement pstateTb = conn.prepareStatement(this.getTableSql());
            ResultSet resultsTb = pstateTb.executeQuery();
            while (resultsTb.next()) {
                String tableName = resultsTb.getString(fieldEnum.getTableName());
                if (null != tables && !tables.isEmpty()) {
                    if (tables.contains(tableName)) {
                        tableNameMap.put(tableName, resultsTb.getString(fieldEnum.getTableComment()));
                    }
                } else {
                    tableNameMap.put(tableName, resultsTb.getString(fieldEnum.getTableComment()));
                }
            }
            for (Map.Entry<String, String> entry : tableNameMap.entrySet()) {
                List<TableField> list = new ArrayList<TableField>();
                PreparedStatement pstate = conn.prepareStatement(this.getFieldSql(entry.getKey()));
                ResultSet results = pstate.executeQuery();
                while (results.next()) {
                    String field = results.getString(fieldEnum.getFieldName());
                    String type = results.getString(fieldEnum.getFieldType());
                    TableField tbField = new TableField();
                    tbField.setFieldName(field);
                    tbField.setType(type);
                    tbField.setDataBaseType(typeConvert(type));
                    tbField.setKeyFlag(PRI.equals(results.getString(fieldEnum.getFieldKey())));
                    tbField.setIsNull(results.getString(fieldEnum.getFieldIsNull()));
                    tbField.setComment(
                            Optional.ofNullable(results.getString(fieldEnum.getFieldComment())).orElse(field));
                    list.add(tbField);
                }
                listVo.add(new TableAttr(list, entry.getValue(), entry.getKey(),
                        BuildTextUtils.hump(entry.getKey(), includePrefix)));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return listVo;
    }
}
